#include "audio.h"
#include "display.h"
#include <arduinoFFT.h>

#include "AudioFileSourceICYStream.h"
#include "AudioFileSourceBuffer.h"
#include "AudioGeneratorMP3.h"
#include "AudioOutputI2S.h"

i2s_config_t inmp441_i2s_config = {
    .mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX),
    .sample_rate = SAMPLE_RATE,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
    .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
    // note：！！！
    .communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_STAND_I2S),
    .intr_alloc_flags = 0,
    .dma_buf_count = 16, // buffer 的数量
    .dma_buf_len = 64,   // buffer的大小，单位是i2s_bits_per_sample_t 采样位数，越小播放需要越及时时延越小，否则相反
    .use_apll = false};

const i2s_pin_config_t inmp441_gpio_config = {
    .bck_io_num = INMP441_SCK,
    .ws_io_num = INMP441_WS,
    .data_out_num = I2S_PIN_NO_CHANGE,
    .data_in_num = INMP441_SD};

i2s_config_t max98357_i2s_config = {
    .mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_TX),
    .sample_rate = SAMPLE_RATE,
    .bits_per_sample = i2s_bits_per_sample_t(16),
    .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
    // note：！！！
    .communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_STAND_MSB),
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count = 16,
    .dma_buf_len = 64};

const i2s_pin_config_t max98357_gpio_config = {
    .bck_io_num = MAX98357_BCLK,
    .ws_io_num = MAX98357_LRC,
    .data_out_num = MAX98357_DIN,
    .data_in_num = -1};

void audio_init()
{
  audio_inmp441_init();
  audio_max98357_init();

  // 可有可无
  // i2s_start(I2S_NUM_0);
  // i2s_start(I2S_NUM_1);
}

void audio_test_mic_to_speaker()
{
  size_t bytes_read;

  uint16_t data[1024];

  esp_err_t result = i2s_read(I2S_NUM_0, &data, sizeof(data), &bytes_read, portMAX_DELAY);

  size_t bytes_write;

  result = i2s_write(I2S_NUM_1, &data, sizeof(data), &bytes_write, portMAX_DELAY);
  // Serial.printf("read: %d, write: %d, %d\r\n", bytes_read, bytes_write, result);
}

void audio_inmp441_init()
{
  i2s_driver_install(I2S_NUM_0, &inmp441_i2s_config, 0, NULL);
  i2s_set_pin(I2S_NUM_0, &inmp441_gpio_config);

  // 不需要
  // i2s_start(I2S_NUM_0);
  // delay(500);
}

void audio_inmp441_test()
{
  int16_t sBuffer[256];
  size_t bytesIn = 0;
  esp_err_t result = i2s_read(I2S_NUM_0, &sBuffer, sizeof(sBuffer), &bytesIn, portMAX_DELAY);
  if (result == ESP_OK)
  {
    audio_inmp441_test_print(sBuffer, bytesIn);
  }
}

void audio_inmp441_test_print(int16_t *buffer, size_t bytes)
{
  int samples_read = bytes / 2;
  if (samples_read > 0)
  {
    float mean = 0;
    for (int i = 0; i < samples_read; ++i)
    {
      mean += (buffer[i]);
    }
    mean /= samples_read;
    Serial.println(mean);
    delay(10);
  }
}

void audio_max98357_init()
{
  i2s_driver_install(I2S_NUM_1, &max98357_i2s_config, 0, NULL);
  i2s_set_pin(I2S_NUM_1, &max98357_gpio_config);
}

// Called when a metadata event occurs (i.e. an ID3 tag, an ICY block, etc.
void MDCallback(void *cbData, const char *type, bool isUnicode, const char *string)
{
  const char *ptr = reinterpret_cast<const char *>(cbData);
  (void)isUnicode; // Punt this ball for now
  // Note that the type and string may be in PROGMEM, so copy them to RAM for printf
  char s1[32], s2[64];
  strncpy_P(s1, type, sizeof(s1));
  s1[sizeof(s1) - 1] = 0;
  strncpy_P(s2, string, sizeof(s2));
  s2[sizeof(s2) - 1] = 0;
  Serial.printf("METADATA(%s) '%s' = '%s'\n", ptr, s1, s2);
  Serial.flush();
}

// Called when there's a warning or error (like a buffer underflow or decode hiccup)
void StatusCallback(void *cbData, int code, const char *string)
{
  const char *ptr = reinterpret_cast<const char *>(cbData);
  // Note that the string may be in PROGMEM, so copy it to RAM for printf
  char s1[64];
  strncpy_P(s1, string, sizeof(s1));
  s1[sizeof(s1) - 1] = 0;
  Serial.printf("STATUS(%s) '%d' = '%s'\n", ptr, code, s1);
  Serial.flush();
}

AudioGeneratorMP3 *mp3;
AudioOutputI2S *out;
void audio_max98357_mp3_init()
{
  audioLogger = &Serial;
  out = new AudioOutputI2S(I2S_NUM_1); // 因为0 mic用了
  out->SetPinout(MAX98357_BCLK, MAX98357_LRC, MAX98357_DIN);
  out->SetChannels(1); // 单声道
  out->SetGain(GAIN_DEFAULT);   // 设置初始音量
  mp3 = new AudioGeneratorMP3();
  mp3->RegisterStatusCB(StatusCallback, (void *)"mp3");
  // mp3->begin(buff, out);
}

void audio_max98357_mp3_start()
{
  // mp3->begin(buff, out);
}

void audio_max98357_mp3_play(const char *url, bool force)
{
  if (mp3->isRunning())
  {
    if (force)
      mp3->stop();
    else
      return;
  }
  AudioFileSourceICYStream *file = new AudioFileSourceICYStream(url);
  file->RegisterMetadataCB(MDCallback, (void *)"ICY");
  AudioFileSourceBuffer *buff = new AudioFileSourceBuffer(file, 2048);
  buff->RegisterStatusCB(StatusCallback, (void *)"buffer");

  mp3->begin(buff, out);
}

void audio_max98357_mp3_loop()
{
  if (mp3->isRunning())
    if (!mp3->loop())
    {
      mp3->stop();
      Serial.println("mp3 loop stop");
    }
}

void audio_max98357_mp3_stop()
{
  if (mp3->isRunning())
    mp3->stop();
}

#define FFT_SIZE 256     // FFT分析点数(32×8的点阵适配)
#define MAX_VOLUME 10000 // 音量最大值(根据实际测试调整)
// note:调整以下2个参数可以决定行高和灵敏度
#define LED_HEIGHT_MULTIPLIER 0.8 // 高度缩放因子
#define BRIGHTNESS_DECAY 34       // 亮度衰减因子
// FFT相关
ArduinoFFT<double> FFT = ArduinoFFT<double>();
double vReal[FFT_SIZE];
double vImag[FFT_SIZE];
double previousBands[MATRIX_WIDTH]; // 历史频带值(用于平滑过渡)
// 预设频率色带
DEFINE_GRADIENT_PALETTE(soundwave_gp){
    0, 30, 30, 180,   // 午夜蓝
    32, 0, 120, 220,  // 宝石蓝
    64, 0, 200, 160,  // 绿松石
    96, 180, 220, 0,  // 柠檬黄
    128, 255, 150, 0, // 橙
    160, 255, 80, 30, // 珊瑚红
    192, 220, 0, 150, // 品红
    224, 180, 0, 200, // 紫
    255, 80, 0, 180   // 深紫
};
CRGBPalette16 spectrumPalette = soundwave_gp;
void audio_spectrum_anim()
{
  static float maxDecay = 0.1; // 峰值衰减速率(0.1-0.3之间调整灵敏度)
  static int16_t rawAudioBuffer[FFT_SIZE];

  // 1. 从INMP441采集原始音频样本
  size_t bytesRead = 0;
  i2s_read(I2S_NUM_0, rawAudioBuffer, sizeof(rawAudioBuffer), &bytesRead, portMAX_DELAY);
  // audio_inmp441_test_print(rawAudioBuffer, bytesRead);

  // 2. 准备FFT输入数据（DC偏移校准）
  int16_t sum = 0;
  for (int i = 0; i < FFT_SIZE; i++)
  {
    sum += rawAudioBuffer[i];
  }
  int16_t dcOffset = sum / FFT_SIZE;

  // 3. 填充FFT输入数组并移除DC偏移
  for (int i = 0; i < FFT_SIZE; i++)
  {
    vReal[i] = (double)(rawAudioBuffer[i] - dcOffset);
    vImag[i] = 0.0;
  }

  // 4. 应用汉宁窗减少频谱泄露
  FFT.windowing(vReal, FFT_SIZE, FFT_WIN_TYP_HAMMING, FFT_FORWARD);

  // 5. 执行FFT计算
  FFT.compute(vReal, vImag, FFT_SIZE, FFT_FORWARD);
  FFT.complexToMagnitude(vReal, vImag, FFT_SIZE);

  // 6. 根据人耳特性分组（对数分布）
  const float bandsPerOctave = 3.0;                 // 每倍频程3个频带
  const int minFrequency = 80;                      // 最低分析频率
  int bandwidth = (SAMPLE_RATE / 2) / MATRIX_WIDTH; // 初始带宽
  uint8_t bandHeight[MATRIX_WIDTH] = {0};           // 频带高度存储

  for (int col = 0; col < MATRIX_WIDTH; col++)
  {
    // 6.1 对数映射频带（低频分辨率更高）
    double freqLow = minFrequency * pow(2, col / bandsPerOctave);
    double freqHigh = minFrequency * pow(2, (col + 1) / bandsPerOctave);

    // 6.2 计算频带索引范围
    int startBin = max(1, int(freqLow * FFT_SIZE / SAMPLE_RATE));
    int endBin = min(FFT_SIZE / 2, int(freqHigh * FFT_SIZE / SAMPLE_RATE));

    // 6.3 寻找频带内峰值（而非平均值，响应更快）
    double bandPeak = 0;
    for (int bin = startBin; bin < endBin; bin++)
    {
      if (vReal[bin] > bandPeak)
        bandPeak = vReal[bin];
    }

    // 6.4 动态归一化（对数压缩）
    bandPeak = log10(1 + bandPeak) / log10(1 + MAX_VOLUME);

    // 6.5 指数平滑消除抖动
    bandPeak = max(bandPeak, previousBands[col] * 0.7); // 新值需超过历史值的70%
    previousBands[col] = bandPeak * (1 - maxDecay) + previousBands[col] * maxDecay;

    // 6.6 映射到LED高度(0~7)
    bandHeight[col] = (uint8_t)constrain(
        round(previousBands[col] * MATRIX_HEIGHT * LED_HEIGHT_MULTIPLIER), 0, MATRIX_HEIGHT - 1);
  }

  // 7. 可视化到LED矩阵
  displayManager->clear();
  // 修改频带映射逻辑
  for (int x = 0; x < MATRIX_WIDTH; x++)
  {
    // 单声道统一映射
    int display_x = x;

    // 调整Y轴坐标为正向显示
    int display_y = bandHeight[x];

    // 单声道颜色渐变（从蓝到红）
    uint8_t colorIndex = map(x, 0, MATRIX_WIDTH - 1, 0, 255);
    // uint8_t colorIndex = map(x, 0, MATRIX_WIDTH - 1, 0, 255 * 2); // 双倍色相范围
    // colorIndex = colorIndex % 255;                                // 循环渐变

    // 动态亮度计算（底部最亮）
    uint8_t brightness = map(display_y, 0, MATRIX_HEIGHT - 1, 80, 255);

    // 绘制频谱柱
    for (int y = 0; y <= display_y; y++)
    {
      displayManager->drawPixel(
          display_x,
          MATRIX_HEIGHT - 1 - y, // 正向坐标
          ColorFromPalette(spectrumPalette, colorIndex, brightness));
      brightness = qsub8(brightness, BRIGHTNESS_DECAY); // 亮度递减
    }
  }
  // for (int x = 0; x < MATRIX_WIDTH; x++)
  // {
  //   // 7.1 计算颜色（根据频率映射）
  //   uint8_t colorIndex = map(min(x, MATRIX_WIDTH - 1), 0, MATRIX_WIDTH - 1, 0, 255);
  //   CRGB columnColor = ColorFromPalette(spectrumPalette, colorIndex);

  //   // 7.2 绘制频谱柱(带下落轨迹效果)
  //   for (int y = 0; y < MATRIX_HEIGHT; y++)
  //   {
  //     if (y <= bandHeight[x])
  //     {
  //       // 频谱柱主体
  //       uint8_t brightness = (y == bandHeight[x]) ? 255 : (128 + y * 30);
  //       displayManager->drawPixel(x, MATRIX_HEIGHT - 1 - y, ColorFromPalette(spectrumPalette, colorIndex, brightness));
  //     }
  //     /* 取消注释开启下落轨迹
  //     else if (y <= bandHeight[x] + 2)
  //     {
  //       // 尾部拖影效果
  //       uint8_t trailing = max(0, 150 - (y - bandHeight[x]) * 70);
  //       displayManager->drawPixel(x, MATRIX_HEIGHT - 1 - y,
  //                                 ColorFromPalette(spectrumPalette, colorIndex, trailing));
  //     }
  //     */
  //   }
  // }
  displayManager->show();
}